{

    class Component {
        constructor (option) {
            let _this = this;
            let elementDragged = null;
            _this.stage = option.stage;
            _this.widget = option.widget;

            _this.widget.setAttribute('draggable', 'true');
            _this.widget.addEventListener('dragstart', function(e) { 
                if ( this.getAttribute('data-container') === null ) {
                    let hashId = util.randomId();
                    let id = "dom" + hashId;
                    let elementTemplate = "<div id='"+ id  + "_ele' class='temp-html-warp' >" + 
                    eval("window.TEMPLATE." + this.getAttribute('data-type') + ".elementTemplate") + 
                    "</div>";
                    let prototypeTemplate = "<div id='"+ id + "_pro' class='temp-pro-warp' style='display:none'>" + 
                    eval("window.TEMPLATE." + this.getAttribute('data-type') + ".prototypeTemplate") + 
                    "</div>";
                    let template =  "<div id='"+ id +"' data-id='" + hashId + "' data-path='"+ this.getAttribute('data-type') +"'>" + elementTemplate + prototypeTemplate + "</div>";
                    let info = {
                        type : "component",
                        path : "window.TEMPLATE." + this.getAttribute('data-type'),
                        html : template,
                        id : hashId
                    };
                    e.dataTransfer.setData('component', JSON.stringify(info));
                } else {
                    let info = {
                        type : "container", 
                        containerType : this.getAttribute('data-type')
                    };
                    e.dataTransfer.setData('component', JSON.stringify(info));
                }
                elementDragged = this;
            });
            _this.widget.addEventListener('dragend', function(e) {
                elementDragged = null;
            });
        }
    }

    class FormPage {
        constructor (option) {
           this.option = option;
        }

        show() {
            let _this = this; 
            let htmlTemplate = window.formTemplate[_this.option.template.name];
            let data = _this.option.data
            let newHtml = _this._compile(htmlTemplate, data);
            _this._newWindow(newHtml);
        }

        getAll() {
            let _this = this; 
            let htmlTemplate = window.formTemplate[_this.option.template.name];
            let data = _this.option.data
            let newHtml = _this._compile(htmlTemplate, data); 
            return newHtml; 
        }

        _compile (htmlTemplate, data) {
            let newHtml = htmlTemplate;
            for (let key in data) {
                newHtml = newHtml.replaceAll('{{'+key+'}}', data[key]);
            } 
            return newHtml;
        }

        _newWindow(html) {
            var win = window.open('', "_blank", ''); 
            win.document.open('text/html', 'replace'); 
            win.opener = null 
            win.document.write(html); 
            win.document.close(); 
        }

    }

    class Stage {
        constructor (stage, proto) {
            let _this = this;
            this.protoCont = proto;
            this.line = {
                x : window.document.createElement("div"),
                y : window.document.createElement("div")
            }
            this.line.x.className = "pos-x-line"
            this.line.y.className = "pos-y-line";
            
            stage.appendChild(this.line.x);
            stage.appendChild(this.line.y);

            stage.addEventListener('dragover', function(e) {
                e.preventDefault();
                e.dataTransfer.dropEffect = 'move';
                return false;
            });

            stage.addEventListener('dragleave', function(e){
                _this.line.x.style.display = "none";
                _this.line.y.style.display = "none";
            });

            stage.addEventListener('drop', function(e) {
                e.preventDefault();
                e.stopPropagation();
                let infoObj = JSON.parse(e.dataTransfer.getData('component'));
                let container = null;
                if ( infoObj.type === "component" ) {
                    container = _this._newComponentElement(infoObj);
                }
                if ( infoObj.type === "container" ) {
                    container = _this._newContainerElement(infoObj);
                }
                this.appendChild(container);
                _this.line.x.style.display = "none"; 
                _this.line.y.style.display = "none";
                if ( infoObj.type === "component" ) _this._initVue(infoObj); 
                if ( infoObj.type === "container" ) {
                    container.style.border = "1px dashed #6698FF";
                    setTimeout(function () {
                        container.style.border = "";
                    },200);
                }
                return false; 
            });

            this.stage = stage;
        }

        getHtml () {
            let html = '';
            let compileEle = document.createElement("div");
            let childNodes = document.querySelector(".canvas-form-warp").cloneNode(true);
            compileEle.appendChild(childNodes);
            util.removeEle(compileEle.querySelector('.pos-x-line'))
                .then(compileEle.querySelector('.pos-y-line'))
                .then(compileEle.querySelectorAll('.layer'))
                .then(compileEle.querySelectorAll('.temp-pro-warp'))
                .then(compileEle.querySelectorAll('.container-layer'))

            let pathEles = compileEle.querySelectorAll("[data-replace]");
            let warpEles = compileEle.querySelectorAll(".temp-html-warp");

            //替换成实际html代码。
            pathEles.forEach((item)=>{
               let realTemplate = eval('window.TEMPLATE.' + item.getAttribute("data-replace") + '.realTemplate' );
               if ( realTemplate ) {
                    util.replaceEle(item, realTemplate);
               }
            });

            //干掉设计器中的包裹层。
            warpEles.forEach((item)=>{
                util.replaceEle(item, item.innerHTML);
            });

            //得到表单上的所有注册元素，并插入数据信息。
            let regElements = compileEle.querySelectorAll("[data-id]");
            regElements.forEach((item)=> {
                let data = window['vue' + item.getAttribute('data-id')].$data;
                let dataJson = {};
                for (let key in data) {
                    dataJson[key] = data[key];
                }
                item.setAttribute("data-datajson", JSON.stringify(dataJson))
            });

            console.log(compileEle);
            return compileEle.querySelector('.canvas-form-warp').innerHTML;
        }

        _listenChildDrop (domObj) {
            let _this = this;
            domObj.addEventListener('dragover', function (event) {
                 event.preventDefault();
                 event.stopPropagation(); 
                 let e = event || window.event;
                 let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
                 let scrollY = document.documentElement.scrollTop || document.body.scrollTop;
                 let x = e.pageX || e.clientX + scrollX;
                 let y = e.pageY || e.clientY + scrollY; 
                 let splitPoint = util.getTop(this) + ( this.offsetHeight / 2 ); 
            
                 if ( _this.line.x.style.display !== "block" ) _this.line.x.style.display = "block";
                 if ( y < splitPoint ) {
                    _this.line.x.style.top = this.offsetTop + "px"; 
                 }
                 if ( y > splitPoint ) { 
                    _this.line.x.style.top = this.offsetTop + this.offsetHeight + "px";
                 }
                 
            });

            domObj.addEventListener('drop', function (event) {
                event.preventDefault();
                event.stopPropagation(); 
                let e = event || window.event;
                let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
                let scrollY = document.documentElement.scrollTop || document.body.scrollTop;
                let x = e.pageX || e.clientX + scrollX;
                let y = e.pageY || e.clientY + scrollY;
                let splitPoint = util.getTop(this) + ( this.offsetHeight / 2 );

                let infoObj = JSON.parse(e.dataTransfer.getData('component'));
                let container = null;
                if ( infoObj.type === "component" ) {
                    container = _this._newComponentElement(infoObj);
                }
                if ( infoObj.type === "container" ) {
                    container = _this._newContainerElement(infoObj);
                }

                if ( y < splitPoint ) {
                    domObj.parentNode.insertBefore(container,domObj);  
                }
                if ( y > splitPoint ) {
                    domObj.parentNode.insertBefore(container,domObj.nextSibling); 
                }

                _this.line.x.style.display = "none"; 
                if ( infoObj.type === "component" ) _this._initVue(infoObj);
                return false; 
            });

        }

        _listenContainerDrop (domObj) {
            let _this = this;
            domObj.addEventListener('dragover', function (event) {
                 event.preventDefault();
                 event.stopPropagation(); 
                 let e = event || window.event;
                 let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
                 let scrollY = document.documentElement.scrollTop || document.body.scrollTop;
                 let x = e.pageX || e.clientX + scrollX;
                 let y = e.pageY || e.clientY + scrollY; 
                 let splitPoint = util.getTop(this) + ( this.offsetHeight / 2 );  

                 _this.line.y.style.height = this.offsetHeight + "px";
                 if ( _this.line.x.style.display !== "block" ) _this.line.x.style.display = "block"; 
                 if ( _this.line.y.style.display !== "block" ) _this.line.y.style.display = "block"; 

                 _this.line.x.style.top = e.clientY - 45 + "px";
                 _this.line.y.style.top = e.clientY - (this.offsetHeight ) + "px";
                 _this.line.y.style.left = e.clientX - (_this.stage.offsetWidth + 25 ) + "px";
            });

            domObj.addEventListener('drop', function (event) {
                event.preventDefault();
                event.stopPropagation(); 
                let e = event || window.event;
                let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
                let scrollY = document.documentElement.scrollTop || document.body.scrollTop;
                let x = e.pageX || e.clientX + scrollX;
                let y = e.pageY || e.clientY + scrollY;
                let splitPoint = util.getTop(this) + ( this.offsetHeight / 2 );   

                let infoObj = JSON.parse(e.dataTransfer.getData('component'));
                let container = null;
                if ( infoObj.type === "component" ) {
                    container = _this._newComponentElement(infoObj); 
                }
                if ( infoObj.type === "container" ) {
                    container = _this._newContainerElement(infoObj);
                }
                this.appendChild(container);
                _this.line.x.style.display = "none"; 
                _this.line.y.style.display = "none"; 
                if ( infoObj.type === "component" ) _this._initVue(infoObj);
                return false;
            });
        }

        _newComponentElement (infoObj) {
            let container = window.document.createElement('div');
            container.className = "view-warp"; 
            container.innerHTML = infoObj.html;
            //container.setAttribute("data-container", "1");
            //container.className = "layout"
            this._listenChildDrop(container);
            this._addLayer(container, infoObj);
            return container;
        }

        _newContainerElement (infoObj) {
             let container = window.document.createElement('div');
             if ( infoObj.containerType ) {
                container.className = infoObj.containerType;
             }
             this._listenContainerDrop(container);
             this._addContainerLayer(container);
             return container;
        }

        _addContainerLayer (domObj) {
            let _this = this;
            let layer = window.document.createElement("div");
            let closeButton = window.document.createElement("div");
            closeButton.className = "close-icon";
            layer.className = "container-layer";
            layer.appendChild(closeButton);
            domObj.appendChild(layer);

            closeButton.addEventListener('click', function (event) {
                 this.parentNode.parentNode.style.border = "1px dashed red";
                 setTimeout(()=>{
                    this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode);
                 },200);
            });
        }

        _addLayer (domObj, infoObj) {
            let _this = this;
            let layer = window.document.createElement("div");
            let closeButton = window.document.createElement("div");
            closeButton.className = "close-icon";
            layer.className = "layer";
            layer.appendChild(closeButton);
            domObj.appendChild(layer);  

            layer.addEventListener('click', function (event) { 
                event.stopPropagation(); 
                window.document.querySelectorAll('.layer').forEach(function (item) {
                    item.style.backgroundColor = "rgba(0, 0, 0, 0)";
                });
                window.document.querySelectorAll('.temp-pro-warp').forEach(function (item) {
                    item.style.display = "none";
                });

                this.style.backgroundColor = "rgba(0, 0, 0, 0.05)";
                let proDomId = "dom" + infoObj.id + "_pro";
                let proDom = document.getElementById(proDomId);

                proDom.style.backgroundColor = "#fff";
                proDom.style.border = "1px solid #ddd";
                proDom.style.display = "block";
                proDom.style.position = "fixed";
                proDom.style.top = _this.protoCont.offsetTop + "px";
                proDom.style.right = "0px"; 
                proDom.style.width = _this.protoCont.offsetWidth + "px"; 
                proDom.style.zIndex = "10"; 
                proDom.style.padding = "10px";
                _this.protoCont.style.height = "0px";

            }); 

            closeButton.addEventListener('click', function (event) { 
                event.stopPropagation(); 
                this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode);
                let vueId = "vue" + infoObj.id;
                window[vueId] = undefined;
            });

        }

        _initVue(infoObj) {
            let domId = "dom" + infoObj.id;
            let vueId = "vue" + infoObj.id;
            if ( !window[vueId] ) {
                let _constructor = eval(infoObj.path+"._constructor"); 
                let tObj = _constructor();
                let Obj = {
                    el : "#" + domId
                }
                Obj = Object.assign(tObj, Obj);
                window[vueId] = new Vue(Obj);
            }
        }

    } 

    const _init = () => {

        let widgetDoms = document.querySelectorAll("#dragWidget li");
        let stageDom = document.querySelector(".canvas-form-warp");
        let protoDom = document.querySelector(".set-property");
        let widgetObjs = [];

        //加载组件
        if ( window.componentList ) {
            window.componentList.forEach(function (item) {
                window.util.loadJS(window.CONFIG.TEMPLATE_BASE_URL + item);
            });
        }

        //实例化舞台
        let stageObj = new Stage(stageDom, protoDom); 
        //实例化组件
        widgetDoms.forEach((o)=>{
            widgetObjs.push(new Component({
                widget : o,
                stage : stageObj
            }));
        });

        //初始化舞台属性设置器
        let stageSetButton = window.document.querySelector("#stageSet");
        let stageWarpDom = window.document.querySelector(".canvas-warp");
        let stageSetWarpDom = window.document.querySelector("#stageSetBox");
        let stageWidthInput = window.document.querySelector("#stageWidthInput");
        let previewFormButton = window.document.querySelector("#previewFormButton");
        let generateFormButton = window.document.querySelector("#generateFormButton");
        let stageFormTitle = document.querySelector('#stageFormTitle');
        stageSetButton.addEventListener('click', function(event) {
            window.document.querySelectorAll('.temp-pro-warp').forEach(function (item) {
                item.style.display = "none";
            });
            stageSetWarpDom.style.display = "block";  
        });
        previewFormButton.addEventListener('click', function() {
            let newPage = new FormPage({
                template : {
                    path : 'form-template/default',
                    name : 'default'
                }, 
                data : {
                    title : stageFormTitle.value,
                    width : stageWidthInput.value,
                    html : stageObj.getHtml()
                }
            });
            newPage.show();
        });
        generateFormButton.addEventListener('click', function() {
            let newPage = new FormPage({
                template : {
                    path : 'form-template/default',
                    name : 'default'
                }, 
                data : {
                    title : stageFormTitle.value,
                    width : stageWidthInput.value,
                    html : stageObj.getHtml()
                }
            });
            $.ajax({
                url : '/generate',
                type : 'POST',
                data :  {
                    'html' : newPage.getAll() ,
                    'output' : './form-template/form.html'
                },
                async : true,
                success : function () { 
                    window.open("/form-template/form.html"); 
                }
            })
        });
        stageWidthInput.addEventListener('input', function() {
            stageWarpDom.style.width = this.value;
        });

        window.stageObj = stageObj;
    }

    _init();
}